<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>颗粒时钟</title>
</head>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    html,body{
        overflow: hidden;
    }
    canvas {
        background: radial-gradient(#fff, #8c738c);
        display: block;
    }
</style>
<body>
   <canvas></canvas>
</body>
</html>
<script>
    const canvas = document.querySelector('canvas')
    const ctx = canvas.getContext('2d', {
        willReadFrequently: true
    })
    //颗粒数量
    const PARTICLE_NUM = 15000
    //绘制的文本
    let text = ''
    //绘制的文本字体大小
    const FONT_SIZE = 140;
    //颗粒对象形成的数组
    const particles = new Array(PARTICLE_NUM)
    //颗粒颜色
    const COLOR = '#5445544d';
    //随机尺寸范围
    const SIZES = [2, 7];
    //开始移动时间
    let startMoveTime = 0;
    //获取【min,max】范围内的随机整数
    function getRandom(min, max) {
        return Math.floor(Math.random() * (max + 1 - min) + min);
    }
    //初始化
    function init() {
        //初始话画布
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        //初始化颗粒对象数组
        const cx = canvas.width / 2,
            cy = canvas.height / 2;
        for (let i = 0; i < particles.length; i++) {
            const rad = Math.random() * 2 * Math.PI;
            const size = getRandom(SIZES[0], SIZES[1]);
            const r = canvas.height / 2;
            particles[i] = {
                sx: cx + Math.cos(rad) * r,
                sy: cy + Math.sin(rad) * r,
                x: cx + Math.cos(rad) * r,
                y: cy + Math.sin(rad) * r,
                size
            };
            drawParticle(particles[i])
        }
    }
    //刷新绘制
    function fps() {
        requestAnimationFrame((time) => {
            //要绘制的文本
            const curText = getText();
            if (curText !== text) {
                text = curText;//更新文本
                //文本发生变化，颗粒开始移动
                //更新颗粒的起始坐标
                for (const p of particles) {
                    p.sx = p.x;
                    p.sy = p.y;
                }
                startMoveTime = Date.now()
            }
            //获取像素点
            const imgData = getBMP();
            //更新绘制
            update(imgData);
            fps()
        })
    }
    fps()
    function drawParticle(p) {
        ctx.fillStyle = COLOR;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2,true)
        ctx.closePath();
        ctx.fill()
    }
    function getText() {
        // console.log(new Date().toLocaleString())
        // let day = Date().toLocaleDateString()+ '-' + Date().toLocaleTimeString()
        // return new  day;
        var now = new Date();
        var year = now.getFullYear();       //年
        var month = now.getMonth() + 1;     //月
        var day = now.getDate();            //日
        var hh = now.getHours();            //时
        var mm = now.getMinutes();          //分
        var ss = now.getSeconds();  //获取当前秒
        var clock = year + "-";
        if(month < 10)
            clock += "0";
        clock += month + "-";
        if(day < 10)
            clock += "0";
        clock += day + " ";
        if(hh < 10)
            clock += "0";
        clock += hh + ":";
        if (mm < 10) clock += '0';
        if (ss < 10) clock += '0';
        clock += mm + ":" + ss;
        return(clock);
    }
    function getBMP() {
        const {width, height} = canvas;
        clear()
        ctx.fillStyle = '#fff';
        ctx.textBaseline = 'middle';
        ctx.font = `${FONT_SIZE}px '手札体-简', sans-serif`;
        const textWidth = ctx.measureText(text).width;
        ctx.fillText(text, (width - textWidth) / 2, height / 2);
        const imgData = ctx.getImageData(0, 0, width, height)
        return imgData
    }
    function update(imgData) {
        clear();
        const {width, height, data} = imgData
        const dis = 4
        const pxls = [];
        for (let w = 0; w < width; w += dis) {
            for (let h = 0; h < height; h += dis) {
                const i = (w + h * width) * 4;
                if (data[i] > 10) {
                    pxls.push([w, h]);
                }
            }
        }
        const count = Math.min(particles.length , pxls.length)
        const duration = 400;
        const timeSpan = Date.now() - startMoveTime
        for (let i = 0; i < count; i++) {
            const p = particles[i];
            const sx = p.sx;
            const sy = p.sy;
            const tx = pxls[i][0];
            const ty = pxls[i][1];
            const disX = tx - sx,
                disY = ty - sy;
            let moveX = (disX / duration) * timeSpan,
                moveY = (disY / duration) * timeSpan;
            if (Math.abs(moveX) > Math.abs(disX)){
                moveX = disX
            }
            if (Math.abs(moveY) > Math.abs(disY)){
                moveY = disY
            }
            p.x = sx + moveX;
            p.y = sy + moveY;
            drawParticle(p)
        }
    }
    function clear() {
        const {width , height} = canvas;
        ctx.clearRect(0,0,width,height);
    }
    init();
</script>